/*
PearReview - The peer review assistant.
Copyright (C) 2009  Dimitrij Pankratz, Anton Musichin
http://www.pearreview.com, info@pearreview.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package com.pearreview.reviewerplugin.io;

import java.io.File;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.URI;
import java.util.List;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

import com.pearreview.reviewerplugin.Report;
import com.pearreview.util.review.Aspect;
import com.pearreview.util.review.Finding;
import com.pearreview.util.review.Product;
import com.pearreview.util.review.Severity;

public class XMLReportWriter implements ReportWriter, Serializable {
	
	private static final long serialVersionUID = 8419506617737412388L;
	
	public final static String NAMESPACE = "http://www.pearreview.com";
	public final static String PREFIX = "PearReview";
	
	private DatatypeFactory dtFactory;
	
	@Override
	public void write(Report report, File file) 
			throws TransformerConfigurationException, TransformerException, 
			TransformerFactoryConfigurationError, ParserConfigurationException, 
			DatatypeConfigurationException {
		write(report, new StreamResult(file));
	}
	
	@Override
	public void write(Report report, OutputStream stream) 
			throws TransformerConfigurationException, TransformerException, 
			TransformerFactoryConfigurationError, ParserConfigurationException, 
			DatatypeConfigurationException {
		write(report, new StreamResult(stream));
	}
	
	public void write(Report report, Result result) 
			throws TransformerConfigurationException, TransformerException, 
			TransformerFactoryConfigurationError, ParserConfigurationException, 
			DatatypeConfigurationException {
		dtFactory = DatatypeFactory.newInstance();
		TransformerFactory.newInstance().newTransformer().transform(
				new DOMSource(create(report)), result);
	}
	
	protected Node create(Report report) throws ParserConfigurationException {
		Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
				.newDocument();
		
		doc.appendChild(create(report, doc));
		
		return doc;
	}
	
	protected Node create(Report report, Document doc) {
		Node reportRoot = doc.createElementNS(NAMESPACE, "report");
		reportRoot.setPrefix(PREFIX);
		
		/* report name */
		if(report.getName() != null) {
			Node name = doc.createElementNS(NAMESPACE, "name");
			name.setPrefix(PREFIX);
			Node value = doc.createCDATASection(report.getName());
			name.appendChild(value);
			reportRoot.appendChild(name);
		}
		
		/* report description */
		if(report.getDescription() != null) {
			Node description = doc.createElementNS(NAMESPACE, "description");
			description.setPrefix(PREFIX);
			Node value = doc.createCDATASection(report.getDescription());
			description.appendChild(value);
			reportRoot.appendChild(description);
		}
		
		/* review product */
		if(report.getProduct() != null) {
			Node product = create(report.getProduct(), doc);
			reportRoot.appendChild(product);
		}
		
		/* report findings */
		if(report.getFindings() != null) {
			for(Finding currentFinding: report.getFindings()) {
				if(currentFinding == null)
					continue;
				Node finding = create(currentFinding, doc);
				reportRoot.appendChild(finding);
			}
		}
		
		/* report aspects */
		if(report.getAspects() != null) {
			for (Aspect currentAspect: report.getAspects()) {
				if(currentAspect == null)
					continue;
				Node aspect = create(currentAspect, doc);
				reportRoot.appendChild(aspect);
			}
		}
		
		/* report severities */
		if(report.getSeverities() != null) {
			Node severities = create(report.getSeverities(), doc);
			reportRoot.appendChild(severities);
		}
		
		/* report impression */
		if(report.getImpression() != null) {
			Node impression = doc.createElementNS(NAMESPACE, "impression");
			impression.setPrefix(PREFIX);
			Node value = doc.createCDATASection(report.getImpression());
			impression.appendChild(value);
			reportRoot.appendChild(impression);
		}
		
		/* report recommendation */
		if(report.getRecommendation() != null) {
			Node recommendation = 
					doc.createElementNS(NAMESPACE, "recommendation");
			recommendation.setPrefix(PREFIX);
			Node value = doc.createCDATASection(report.getRecommendation());
			recommendation.appendChild(value);
			reportRoot.appendChild(recommendation);
		}
		
		/* report comments */
		if(report.getComments() != null) {
			Node comments = doc.createElementNS(NAMESPACE, "comments");
			comments.setPrefix(PREFIX);
			Node value = doc.createCDATASection(report.getComments());
			comments.appendChild(value);
			reportRoot.appendChild(comments);
		}
		
		/* report preparation time */
		if(report.getPreparationTime() != null && dtFactory != null) {
			Node preparationTime = 
					doc.createElementNS(NAMESPACE, "preparation-time");
			preparationTime.setPrefix(PREFIX);
			Duration pt = dtFactory.newDuration(report.getPreparationTime());
			Text value = doc.createTextNode(pt.toString());
			preparationTime.appendChild(value);
			reportRoot.appendChild(preparationTime);
		}
		
		return reportRoot;
	}
	
	protected Node create(Product product, Document doc) {
		Node productRoot = doc.createElementNS(NAMESPACE, "product");
		productRoot.setPrefix(PREFIX);
		
		/* product name */
		if(product.getName() != null) {
			Node name = doc.createElementNS(NAMESPACE, "name");
			name.setPrefix(PREFIX);
			Node value = doc.createCDATASection(product.getName());
			name.appendChild(value);
			productRoot.appendChild(name);
		}
		
		/* product reference */
		if(product.getReference() != null) {
			for (String currentReference: product.getReference()) {
				if(currentReference == null)
					continue;
				Node reference = doc.createElementNS(NAMESPACE, "reference");
				reference.setPrefix(PREFIX);
				Node value = doc.createCDATASection(currentReference);
				reference.appendChild(value);
				productRoot.appendChild(reference);
			}
		}
		
		/* product version */
		if(product.getVersion() != null) {
			Node version = doc.createElementNS(NAMESPACE, "version");
			version.setPrefix(PREFIX);
			Node value = doc.createCDATASection(product.getVersion());
			version.appendChild(value);
			productRoot.appendChild(version);
		}
		
		return productRoot;
	}
	
	protected Node create(Aspect aspect, Document doc) {
		Node aspectRoot = doc.createElementNS(NAMESPACE, "aspect");
		aspectRoot.setPrefix(PREFIX);
		
		/* aspect id */
		if(aspect.getId() != null) {
			Node id = doc.createElementNS(NAMESPACE, "id");
			id.setPrefix(PREFIX);
			Node value = doc.createTextNode(aspect.getId());
			id.appendChild(value);
			aspectRoot.appendChild(id);
		}
		
		/* aspect category */
		if(aspect.getCategory() != null) {
			Node category = doc.createElementNS(NAMESPACE, "category");
			category.setPrefix(PREFIX);
			Node value = doc.createCDATASection(aspect.getCategory());
			category.appendChild(value);
			aspectRoot.appendChild(category);
		}
		
		/* aspect directive */
		if(aspect.getDirective() != null) {
			Node directive = doc.createElementNS(NAMESPACE, "directive");
			directive.setPrefix(PREFIX);
			Node value = doc.createCDATASection(aspect.getDirective());
			directive.appendChild(value);
			aspectRoot.appendChild(directive);
		}
		
		/* aspect description */
		if(aspect.getDescription() != null) {
			Node description = doc.createElementNS(NAMESPACE, "description");
			description.setPrefix(PREFIX);
			Node value = doc.createCDATASection(aspect.getDescription());
			description.appendChild(value);
			aspectRoot.appendChild(description);
		}
		
		return aspectRoot;
	}
	
	protected Node create(List<Severity> sev, Document doc) {
		Node severitiesRoot = doc.createElementNS(NAMESPACE, "severities");
		severitiesRoot.setPrefix(PREFIX);
		
		for(Severity currentSeverity: sev) {
			if(currentSeverity == null)
				continue;
			Node severity = doc.createElementNS(NAMESPACE, "severity");
			severity.setPrefix(PREFIX);
			Node value = doc.createCDATASection(currentSeverity.getName());
			severity.appendChild(value);
			severitiesRoot.appendChild(severity);
		}
		
		return severitiesRoot;
	}
	
	protected Node create(Finding finding, Document doc) {
		Node findingRoot = doc.createElementNS(NAMESPACE, "finding");
		findingRoot.setPrefix(PREFIX);
		
		/* finding id */
		if(finding.getId() != null) {
			Node id = doc.createElementNS(NAMESPACE, "id");
			id.setPrefix(PREFIX);
			Node value = doc.createTextNode(finding.getId());
			id.appendChild(value);
			findingRoot.appendChild(id);
		}
		
		/* finding severity */
		if(finding.getSeverity().getName() != null) {
			Node severity = doc.createElementNS(NAMESPACE, "severity");
			severity.setPrefix(PREFIX);
			Node value = 
					doc.createCDATASection(finding.getSeverity().getName());
			severity.appendChild(value);
			findingRoot.appendChild(severity);
		}
		
		/* finding description */
		if(finding.getDescription() != null) {
			Node description = doc.createElementNS(NAMESPACE, "description");
			description.setPrefix(PREFIX);
			Node value = doc.createCDATASection(finding.getDescription());
			description.appendChild(value);
			findingRoot.appendChild(description);
		}
		
		/* finding reference */
		if(finding.getReferences() != null) {
			for(String currentReference: finding.getReferences()) {
				if(currentReference == null)
					continue;
				Node reference = doc.createElementNS(NAMESPACE, "reference");
				reference.setPrefix(PREFIX);
				Node value = doc.createCDATASection(currentReference);
				reference.appendChild(value);
				findingRoot.appendChild(reference);
			}
		}
		
		/* finding aspect */
		if(finding.getAspects() != null) {
			for(String currentAspect: finding.getAspects()) {
				if(currentAspect == null) 
					continue;
				Node aspect = doc.createElementNS(NAMESPACE, "aspect");
				aspect.setPrefix(PREFIX);
				Node value = doc.createCDATASection(currentAspect);
				aspect.appendChild(value);
				findingRoot.appendChild(aspect);
			}
		}
		
		/* finding external reference */
		if(finding.getExternalReferences() != null) {
			for(URI currentURI: finding.getExternalReferences()) {
				if(currentURI == null) 
					continue;
				Node uri = doc.createElementNS(NAMESPACE, "external-reference");
				uri.setPrefix(PREFIX);
				Node value = doc.createCDATASection(currentURI.toString());
				uri.appendChild(value);
				findingRoot.appendChild(uri);
			}
		}
		
		return findingRoot;
	}
}